package cn.ywang.netty.server;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * NIO 服务
 *
 * @author BeatificWang
 * @date 2019/6/23 11:51
 */
public class Server {

	/**
	 * 服务器端口
	 */
	private int port = 8080;

	/**
	 * 轮询器
	 */
	private Selector selector;

	/**
	 * 缓冲区
	 */
	private ByteBuffer byteBuffer = ByteBuffer.allocate(1024);


	public Server(int port) {
		this.port = port;
		try {
			// 开启通道
			ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
			// 绑定本地地址和端口号
			serverSocketChannel.bind(new InetSocketAddress(port));
			// 设置通道阻塞模式为非阻塞
			serverSocketChannel.configureBlocking(false);
			// 初始化轮询器
			this.selector = Selector.open();
			// 通道注册轮询器,并设置为接收请求状态
			serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void listen() {
		System.out.println("Listening on:" + this.port);
		try {
			// 轮询主线程
			SelectionKey key;
			while (true) {
				this.selector.select();
				Set<SelectionKey> keys = this.selector.selectedKeys();
				Iterator<SelectionKey> iterator = keys.iterator();
				// 此处体现同步，每次只处理一个key
				while (iterator.hasNext()) {
					key = iterator.next();
					iterator.remove();
					this.process(key);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 处理SelectionKey，每个SelectionKey代表一种状态
	 * key.channel()----多路复用
	 *
	 * @param key
	 */
	private void process(SelectionKey key) throws Exception {
		if (key.isAcceptable()) {
			ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
			SocketChannel channel = serverChannel.accept();
			channel.configureBlocking(false);
			// 当数据准备就绪的时候，将阻塞状态修改为可读
			key = channel.register(this.selector, SelectionKey.OP_READ);
		}
		else if (key.isReadable()) {
			SocketChannel channel = (SocketChannel) key.channel();
			int length = channel.read(this.byteBuffer);
			if (length > 0) {
				this.byteBuffer.flip();
				String content = new String(this.byteBuffer.array(), 0, length);
				key = channel.register(this.selector, SelectionKey.OP_WRITE);
				// 在key上携带一个附件
				key.attach(content);
				System.out.println("读取内容：" + content);
			}
		}
		else if (key.isWritable()) {
			SocketChannel channel = (SocketChannel) key.channel();
			String content = (String) key.attachment();
			channel.write(ByteBuffer.wrap(("输出：" + content).getBytes()));
			channel.close();
		}
	}

	public static void main(String[] args) {
		new Server(8080).listen();
	}
}
